#pragma once
/*
该模块给服务器的主机调用负责处理来自服务中心主机的信息的，提供Rpc服务的主机都是基于长连接的，如果该主机和服务中心主机断开
连接，那么认为该主机提供的Rpc服务下线了。

模块说明：对应提供服务方来说该模块负责管理提供Rpc服务的主机信息，比如一台主机能够提供Add和Div服务调用
那么该模块就记录该主机的ip和port还有和服务中心主机的连接对象Connection对象

对于请求Rpc方来说，管理请求方请求过哪些主机的哪些服务，这样将来提供服务的主机下线可以通知请求过的主机提供对应Rpc服务的
主机已经下线了
*/
#include "../Net.hpp"
#include "../Message.hpp"
#include <set>
namespace Rpc_Manage
{
    class RcpManager
    {
    public:
        using RpcMPtr = std::shared_ptr<RcpManager>;
        struct RpcProvider
        {
            using RpcProviderPtr = std::shared_ptr<RpcProvider>;
            std::mutex _mutex;
            Abstract::BaseConnection::ConPtr _ConPtr; // 连接对象
            Message::Address Host;                    // 连接主机的ip和port
            std::set<std::string> _Methods;           // 主机能够提供的Rpc服务名称
            RpcProvider(const Abstract::BaseConnection::ConPtr &Cptr, const Message::Address &Add)
                : _ConPtr(Cptr), Host(Add)
            {
            }
            // 加锁防止多个监听线程同时添加服务名称
            void AddMethod(const std::string &method)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                _Methods.insert(method);
            }
        };
        // 添加一个提供Rpc服务的主机信息provider对象
        RpcProvider::RpcProviderPtr AddRpcProvider(const Abstract::BaseConnection::ConPtr &Cptr, const Message::Address &Add, const std::string &method)
        {
            // 如果是新的主机提供服务那么要添加新的连接对象和provider关系。也要添加服务名称和provider的关系
            // 如果是旧的连接对象和新的方法，那么也要添加服务名称和provider的关系
            RpcProvider::RpcProviderPtr ProPtr;
            {
                std::unique_lock<std::mutex> lock(_mutex);
                if (_ConsForRpc.count(Cptr)) // 如果当前提供服务的主机已经注册过服务
                {
                    ProPtr = _ConsForRpc[Cptr]; // 那到之前的provider对象
                }
                else
                {
                    ProPtr = std::make_shared<RpcProvider>(Cptr, Add); // 如果当前主机第一次进行服务注册
                    _ConsForRpc.insert({Cptr, ProPtr});                // 添加链接对象和对应的pro对象
                }
                _RpcProviders[method].insert(ProPtr); // 添加该服务方法的提供者
            }
            ProPtr->AddMethod(method); // 最后provider添加服务方法
            return ProPtr;
        }
        // 获取一个Rpc连接对象对应的provider对象
        RpcProvider::RpcProviderPtr GetRpcProvider(const Abstract::BaseConnection::ConPtr &Cptr)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            if (_ConsForRpc.count(Cptr))
            {
                return _ConsForRpc[Cptr];
            }
            return RpcProvider::RpcProviderPtr();
        }
        // 服务对象连接断开删除记录
        void DelRpcProvider(const Abstract::BaseConnection::ConPtr &Cptr)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            if (_ConsForRpc.count(Cptr) <= 0)
            {
                return; // 该服务提供对象没有对应的providers说明不是提供服务的主机
            }
            auto Proptr = _ConsForRpc[Cptr];
            for (auto &method : Proptr->_Methods) // 删除该主机提供的服务方法和对应的提供者provider对象的关系
            {
                if (_RpcProviders.count(method))
                {
                    _RpcProviders[method].erase(Proptr);
                }
            }
            _ConsForRpc.erase(Cptr); // 最后删除服务主机和provider的关系
        }
        // 获取该服务的提供主机有哪些
        std::vector<Message::Address> GetProviders(const std::string &method)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            if (_RpcProviders.count(method) <= 0)
            {
                return std::vector<Message::Address>();
            }
            std::vector<Message::Address> v;
            for (auto &ptr : _RpcProviders[method])
            {
                v.push_back(ptr->Host);
            }
            return v;
        }

    private:
        std::mutex _mutex; // 加锁保护服务中心主机可能会有多个线程去监听Rpc连接
        // Rpc服务名称和能够提供该服务的Provider对象
        std::unordered_map<std::string, std::set<RpcProvider::RpcProviderPtr>> _RpcProviders;
        // 连接对象对应的Provider对象
        std::unordered_map<Abstract::BaseConnection::ConPtr, RpcProvider::RpcProviderPtr> _ConsForRpc;
    };

    // 记录哪些主机请求过哪些服务主机的哪些服务及哪些服务是新上线的
    class DicoverRManager
    {
    public:
        using DRMPtr = std::shared_ptr<DicoverRManager>;
        struct Discover
        {
            using Dptr = std::shared_ptr<Discover>;
            std::mutex _mutex;
            Abstract::BaseConnection::ConPtr _conn; // 请求过Rpc服务的客户端的连接对象
            std::set<std::string> _Methods;         // 请求过哪些服务
            Discover(const Abstract::BaseConnection::ConPtr &Cptr) : _conn(Cptr)
            {
            }
            void AddMethod(const std::string &method)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                _Methods.insert(method);
            }
        };

        // 添加一个发起Rpc请求的连接对象和Discover的映射关系
        Discover::Dptr AddDiscover(const Abstract::BaseConnection::ConPtr &Cptr, const std::string &method)
        {
            Discover::Dptr ptr;
            std::unique_lock<std::mutex> lock(_mutex);
            {
                if (_ConsToRpc.count(Cptr))
                {
                    ptr = _ConsToRpc[Cptr];
                }
                else
                {
                    ptr = std::make_shared<Discover>(Cptr);
                    _ConsToRpc.insert({Cptr, ptr});
                }
                _RpcDiscovers[method].insert(ptr); // 记录该服务有哪些主机(连接对象)请求过
            }
            ptr->AddMethod(method);
            return ptr;
        }

        // 请求服务端连接断开删除对应的记录信息
        void DelDiscover(const Abstract::BaseConnection::ConPtr &Cptr)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            if (_ConsToRpc.count(Cptr) <= 0)
            {
                return; // 该服务请求对象没有对应的dicovers说明没有请求过任何服务
            }
            auto Disptr = _ConsToRpc[Cptr];
            for (auto &method : Disptr->_Methods) // 删除该主机提供的服务方法和对应的提供者provider对象的关系
            {
                if (_RpcDiscovers.count(method))
                {
                    _RpcDiscovers[method].erase(Disptr);
                }
            }
            _ConsToRpc.erase(Cptr); // 最后删除服务主机和provider的关系
        }
        // 服务提供者上线通知，通知请求过该Rpc服务的客户端 服务提供者上线了
        void OnlineDis(const std::string &Method, const Message::Address &Host)
        {
            Notify(Method, Host, ServiceOptype::SERVICE_Online);
        }
        // 服务提供者下线通知,通知连请求过该服务的客户端，该服务的提供者Hsot下线了
        void OfflineNotify(const std::string &Method, const Message::Address &Host)
        {
            Notify(Method, Host, ServiceOptype::SERVICE_Offline);
        }

    private:
        void Notify(const std::string &Method, const Message::Address &Host, ServiceOptype Type)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            if (_RpcDiscovers.count(Method) <= 0) // 如果该服务在当前的请求者里面没有被请求过那么不做通知
            {
                return;
            }
            // 如果该服务被请求过才去通知
            if (_RpcDiscovers.count(Method))
            {
                // 构建服务上线的message请求给之前请求过该服务的客户端
                auto SerMsg_Req = Message::BuildMessage::build<Message::ServerRequest>();
                SerMsg_Req->SetMtype(Rs_RqType::Service_Req);
                SerMsg_Req->SetUuid(UUID::uuid());
                SerMsg_Req->SetMethod(Method);
                SerMsg_Req->SetHost(Host);
                SerMsg_Req->SetSerOper(Type);
                for (auto &Dis : _RpcDiscovers[Method])
                {
                    Dis->_conn->Send(SerMsg_Req);
                }
            }
        }

    private:
        std::mutex _mutex; // 加锁保护服务中心主机可能会有多个线程去监听Rpc连接
        // 记录哪些服务被哪些主机的连接对象请求过
        std::unordered_map<std::string, std::set<Discover::Dptr>> _RpcDiscovers;
        // 记录连接对象请求过哪些服务
        std::unordered_map<Abstract::BaseConnection::ConPtr, Discover::Dptr> _ConsToRpc;
    };

    // 把管理服务提供者 对象模块和管理 服务请求者的模块整合起来
    // 服务中心主机既要管理提供服务者对象又要管理请求服务的对象
    class PrDiManager
    {
    public:
        using PrDiManagerPtr = std::shared_ptr<PrDiManager>;
        PrDiManager() : _Provider(std::make_shared<RcpManager>()), _Discover(std::make_shared<DicoverRManager>())
        {
        }
        // 处理ServerRequet请求(比如服务查询请求)的回调函数将来注册到Dispather模块中去
        void DealServerRequest(const Abstract::BaseConnection::ConPtr &CPtr, const Abstract::BaseMessage::MesaPtr &Mptr)
        {
            // 1 服务操作请求--注册或者发现
            auto Sptr = std::dynamic_pointer_cast<Message::ServerRequest>(Mptr);
            ServiceOptype Type = Sptr->GetSerOper();
            if (Type == ServiceOptype::SERVICE_Register)
            {
                // 服务注册请求：1记录主机和服务的关系，2通知客户端服务上线
                _Provider->AddRpcProvider(CPtr, Sptr->GetHost(), Sptr->method());
                _Discover->OnlineDis(Sptr->method(), Sptr->GetHost());
                MakeReResponce(CPtr, Mptr);
            }
            else if (Type == ServiceOptype::SERVICE_Discover)
            {
                // 服务发现者即客户端发送一个请求查询有哪些主机可以提供该服务
                _Discover->AddDiscover(CPtr, Sptr->method());
                MakeDisResponce(CPtr, Sptr->method(), Sptr->GetUuid());
            }
            else
            {
                ELOG("没有对应的服务操作");
            }
        }
        // 连接断开时的回调函数
        void ShutDownBack(const Abstract::BaseConnection::ConPtr &CPtr)
        {
            auto Proptr = _Provider->GetRpcProvider(CPtr);
            if (Proptr.get() != nullptr) // 如果是服务提供者连接断开那么通知请求过该方法的客户端
            {
                for (auto &method : Proptr->_Methods)
                {
                    _Discover->OfflineNotify(method, Proptr->Host);
                }
                _Provider->DelRpcProvider(CPtr); // 删除对应的记录信息
                return;
            }
            // 如果是客户端连接断开那么直接删除对应的记录信息即可，不是的话内部函数直接返回
            _Discover->DelDiscover(CPtr);
        }

    private:
        // 构造服务注册请求的回应message
        void MakeReResponce(const Abstract::BaseConnection::ConPtr &CPtr, const Abstract::BaseMessage::MesaPtr &Mptr)
        {
            auto SPtr = Message::BuildMessage::build<Message::ServerReponceJson>();
            SPtr->SetMtype(Rs_RqType::Service_Res);
            SPtr->SetUuid(Mptr->GetUuid());
            SPtr->SetSerOper(ServiceOptype::SERVICE_Register);
            SPtr->Setcode(ResCode::ResCode_Ok);
            CPtr->Send(SPtr);
        }
        // 构建客户端发送的发现服务请求的回应
        void MakeDisResponce(const Abstract::BaseConnection::ConPtr &CPtr, const std::string &method, const std::string &uuid)
        {
            std::vector<Message::Address> v = _Provider->GetProviders(method);
            auto SPtr = Message::BuildMessage::build<Message::ServerReponceJson>();
            SPtr->SetMtype(Rs_RqType::Service_Res);
            SPtr->SetUuid(uuid);
            SPtr->SetSerOper(ServiceOptype::SERVICE_Discover);
            if (v.empty()) // 如果没有主机提供该服务
            {
                // 不设置主机消息
                SPtr->Setcode(ResCode::ResCode_Not_FoundService);
                CPtr->Send(SPtr);
                return;
            }
            // 有主机提供服务把请求的服务名称字段设置进去
            SPtr->Setcode(ResCode::ResCode_Ok);
            SPtr->SetDisResult(v);
            SPtr->SetMethod(method);
            CPtr->Send(SPtr);
        }

    private:
        RcpManager::RpcMPtr _Provider;
        DicoverRManager::DRMPtr _Discover;
    };

}